package com.heima.core;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;


@Component
public class RedisUtil {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private final int count = 10000;

    /**
     * 使用Redis SCAN命令增量迭代数据库中的键，获取匹配指定模式的键列表
     * 该方法通过游标方式分批扫描Redis键空间，相比KEYS命令具有以下优势：
     * - 非阻塞式扫描，避免大数据量导致Redis服务阻塞
     * - 支持分页遍历，通过count参数控制每次迭代的扫描数量
     * - 支持模式匹配，通过pattern参数过滤目标键
     *
     * @param pattern 匹配键名的通配符模式（例如："user:*"）
     *                支持glob风格通配符：
     *                * 匹配任意数量字符
     *                ? 匹配单个字符
     *                [] 匹配指定范围字符
     * @param count   每次扫描的键数量建议值（实际返回数量可能不同）
     *                该参数仅作为扫描次数的优化建议，实际返回键数量可能：
     *                - 在迭代过程中键空间发生变化时出现波动
     *                - 当匹配键密集时返回更多
     *                - 当匹配键稀疏时返回更少
     * @return 匹配到的键名列表，包含UTF-8解码后的字符串结果
     *         返回列表包含：
     *         - 完整匹配pattern的键
     *         - 经过多次迭代收集的全部结果
     *         - 使用UTF-8编码转换后的字符串键值
     */
    public List<String> scan(String pattern, int count){
        return stringRedisTemplate.execute(
                (RedisCallback<List<String>>) connection -> {
                    List<String> matchedKeys = new ArrayList<>();

                    // 构建Redis SCAN操作的配置参数
                    // ScanOptions.builder()链式构建：
                    // 1. match(pattern): 设置键匹配规则
                    // 2. count(count): 指定每次扫描的样本数量
                    // 3. build(): 生成最终的扫描配置对象
                    ScanOptions scanOptions = ScanOptions.scanOptions()
                            .match(pattern)
                            .count(count)
                            .build();

                    // 初始化游标扫描并处理结果集
                    // try-with-resources自动管理游标资源释放
                    // Cursor<byte[]>: Redis返回的原始字节游标
                    try (Cursor<byte[]> cursor = connection.scan(scanOptions)) {
                        // 遍历游标获取所有匹配键
                        // hasNext(): 检查是否还有剩余元素
                        // next(): 获取下一批量的键数据
                        while (cursor.hasNext()) {
                            byte[] keyBytes = cursor.next();
                            // 将Redis返回的原始字节数组转换为字符串
                            // 使用StandardCharsets.UTF_8确保编码一致性
                            String key = new String(keyBytes, StandardCharsets.UTF_8);
                            matchedKeys.add(key);
                        }
                    } catch (Exception e) {
                        // 包装并抛出扫描异常
                        // 包含原始异常信息和堆栈跟踪
                        // 保证异常诊断信息的完整性
                        throw new RuntimeException("Scan keys failed: " + e.getMessage(), e);
                    }

                    return matchedKeys;
                }
        );
    }

    /**
     * 使用指定模式扫描并返回匹配结果列表，默认每次扫描10000条
     *
     * @param pattern 匹配模式字符串，用于定义扫描规则
     * @return 包含匹配结果的字符串列表，具体匹配逻辑由底层实现决定
     */
    public List<String> scan(String pattern){
        return scan(pattern, count);
    }
}
